Passed
Push — master ( 0561d0...39cfaa )
by Justin
04:49 queued 34s
created

icheck.js ➔ option   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 5
rs 9.4285
cc 3
nc 3
nop 3
1
/*!
2
 * iCheck v1.0.1, http://git.io/arlzeA
3
 * =================================
4
 * Powerful jQuery and Zepto plugin for checkboxes and radio buttons customization
5
 *
6
 * (c) 2013 Damir Sultanov, http://fronteed.com
7
 * MIT Licensed
8
 */
9
10
(function($) {
11
12
  // Cached vars
13
  var _iCheck = 'iCheck',
14
    _iCheckHelper = _iCheck + '-helper',
15
    _checkbox = 'checkbox',
16
    _radio = 'radio',
17
    _checked = 'checked',
18
    _unchecked = 'un' + _checked,
19
    _disabled = 'disabled',
20
    _determinate = 'determinate',
21
    _indeterminate = 'in' + _determinate,
22
    _update = 'update',
23
    _type = 'type',
24
    _click = 'click',
25
    _touch = 'touchbegin.i touchend.i',
26
    _add = 'addClass',
27
    _remove = 'removeClass',
28
    _callback = 'trigger',
29
    _label = 'label',
30
    _cursor = 'cursor',
31
    _mobile = /ipad|iphone|ipod|android|blackberry|windows phone|opera mini|silk/i.test(navigator.userAgent);
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
32
33
  // Plugin init
34
  $.fn[_iCheck] = function(options, fire) {
35
36
    // Walker
37
    var handle = 'input[type="' + _checkbox + '"], input[type="' + _radio + '"]',
38
      stack = $(),
39
      walker = function(object) {
40
        object.each(function() {
41
          var self = $(this);
42
43
          if (self.is(handle)) {
44
            stack = stack.add(self);
45
          } else {
46
            stack = stack.add(self.find(handle));
47
          }
48
        });
49
      };
50
51
    // Check if we should operate with some method
52
    if (/^(check|uncheck|toggle|indeterminate|determinate|disable|enable|update|destroy)$/i.test(options)) {
53
54
      // Normalize method's name
55
      options = options.toLowerCase();
56
57
      // Find checkboxes and radio buttons
58
      walker(this);
59
60
      return stack.each(function() {
61
        var self = $(this);
62
63
        if (options == 'destroy') {
64
          tidy(self, 'ifDestroyed');
65
        } else {
66
          operate(self, true, options);
67
        }
68
          // Fire method's callback
69
        if ($.isFunction(fire)) {
70
          fire();
71
        }
72
      });
73
74
    // Customization
75
    } else if (typeof options == 'object' || !options) {
76
77
      // Check if any options were passed
78
      var settings = $.extend({
79
          checkedClass: _checked,
80
          disabledClass: _disabled,
81
          indeterminateClass: _indeterminate,
82
          labelHover: true,
83
          aria: false
84
        }, options),
85
86
        selector = settings.handle,
87
        hoverClass = settings.hoverClass || 'hover',
88
        focusClass = settings.focusClass || 'focus',
89
        activeClass = settings.activeClass || 'active',
90
        labelHover = !!settings.labelHover,
91
        labelHoverClass = settings.labelHoverClass || 'hover',
92
93
        // Setup clickable area
94
        area = ('' + settings.increaseArea).replace('%', '') | 0;
95
96
      // Selector limit
97
      if (selector == _checkbox || selector == _radio) {
98
        handle = 'input[type="' + selector + '"]';
99
      }
100
        // Clickable area limit
101
      if (area < -50) {
102
        area = -50;
103
      }
104
        // Walk around the selector
105
      walker(this);
106
107
      return stack.each(function() {
108
        var self = $(this);
109
110
        // If already customized
111
        tidy(self);
112
113
        var node = this,
114
          id = node.id,
115
116
          // Layer styles
117
          offset = -area + '%',
118
          size = 100 + (area * 2) + '%',
119
          layer = {
120
            position: 'absolute',
121
            top: offset,
122
            left: offset,
123
            display: 'block',
124
            width: size,
125
            height: size,
126
            margin: 0,
127
            padding: 0,
128
            background: '#fff',
129
            border: 0,
130
            opacity: 0
131
          },
132
133
          // Choose how to hide input
134
          hide = _mobile ? {
135
            position: 'absolute',
136
            visibility: 'hidden'
137
          } : area ? layer : {
138
            position: 'absolute',
139
            opacity: 0
140
          },
141
142
          // Get proper class
143
          className = node[_type] == _checkbox ? settings.checkboxClass || 'i' + _checkbox : settings.radioClass || 'i' + _radio,
144
145
          // Find assigned labels
146
          label = $(_label + '[for="' + id + '"]').add(self.closest(_label)),
147
148
          // Check ARIA option
149
          aria = !!settings.aria,
150
151
          // Set ARIA placeholder
152
          ariaID = _iCheck + '-' + Math.random().toString(36).replace('0.', ''),
153
154
          // Parent & helper
155
          parent = '<div class="' + className + '" ' + (aria ? 'role="' + node[_type] + '" ' : ''),
156
          helper;
157
158
        // Set ARIA "labelledby"
159
        if (label.length && aria) {
160
          label.each(function() {
161
            parent += 'aria-labelledby="';
162
163
            if (this.id) {
164
              parent += this.id;
165
            } else {
166
              this.id = ariaID;
167
              parent += ariaID;
168
            }
169
170
            parent += '"';
171
          });
172
        }
173
          // Wrap input
174
        parent = self.wrap(parent + '/>')[_callback]('ifCreated').parent().append(settings.insert);
175
176
        // Layer addition
177
        helper = $('<ins class="' + _iCheckHelper + '"/>').css(layer).appendTo(parent);
178
179
        // Finalize customization
180
        self.data(_iCheck, {o: settings, s: self.attr('style')}).css(hide);
181
        !!settings.inheritClass && parent[_add](node.className || '');
182
        !!settings.inheritID && id && parent.attr('id', _iCheck + '-' + id);
183
        parent.css('position') == 'static' && parent.css('position', 'relative');
184
        operate(self, true, _update);
185
186
        // Label events
187
        if (label.length) {
188
          label.on(_click + '.i mouseover.i mouseout.i ' + _touch, function(event) {
189
            var type = event[_type],
190
              item = $(this);
191
192
            // Do nothing if input is disabled
193
            if (!node[_disabled]) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if !node._disabled is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
194
195
              // Click
196
              if (type == _click) {
197
                if ($(event.target).is('a')) {
198
                  return;
199
                }
200
                operate(self, false, true);
201
202
              // Hover state
203
              } else if (labelHover) {
204
205
                // mouseout|touchend
206
                if (/ut|nd/.test(type)) {
207
                  parent[_remove](hoverClass);
208
                  item[_remove](labelHoverClass);
209
                } else {
210
                  parent[_add](hoverClass);
211
                  item[_add](labelHoverClass);
212
                }
213
              }
214
                if (_mobile) {
215
                event.stopPropagation();
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
216
              } else {
217
                return false;
218
              }
219
            }
220
          });
221
        }
222
          // Input events
223
        self.on(_click + '.i focus.i blur.i keyup.i keydown.i keypress.i', function(event) {
224
          var type = event[_type],
225
            key = event.keyCode;
226
227
          // Click
228
          if (type == _click) {
229
            return false;
230
231
          // Keydown
232
          } else if (type == 'keydown' && key == 32) {
233
            if (!(node[_type] == _radio && node[_checked])) {
234
              if (node[_checked]) {
235
                off(self, _checked);
236
              } else {
237
                on(self, _checked);
238
              }
239
            }
240
              return false;
241
242
          // Keyup
243
          } else if (type == 'keyup' && node[_type] == _radio) {
244
            !node[_checked] && on(self, _checked);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
245
246
          // Focus/blur
247
          } else if (/us|ur/.test(type)) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if us|ur.test(type) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
248
            parent[type == 'blur' ? _remove : _add](focusClass);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
249
          }
250
        });
251
252
        // Helper events
253
        helper.on(_click + ' mousedown mouseup mouseover mouseout ' + _touch, function(event) {
254
          var type = event[_type],
255
256
            // mousedown|mouseup
257
            toggle = /wn|up/.test(type) ? activeClass : hoverClass;
258
259
          // Do nothing if input is disabled
260
          if (!node[_disabled]) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if !node._disabled is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
261
262
            // Click
263
            if (type == _click) {
264
              operate(self, false, true);
265
266
            // Active and hover states
267
            } else {
268
269
              // State is on
270
              if (/wn|er|in/.test(type)) {
271
272
                // mousedown|mouseover|touchbegin
273
                parent[_add](toggle);
274
275
              // State is off
276
              } else {
277
                parent[_remove](toggle + ' ' + activeClass);
278
              }
279
                // Label hover
280
              if (label.length && labelHover && toggle == hoverClass) {
281
282
                // mouseout|touchend
283
                label[/ut|nd/.test(type) ? _remove : _add](labelHoverClass);
284
              }
285
            }
286
              if (_mobile) {
287
              event.stopPropagation();
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
288
            } else {
289
              return false;
290
            }
291
          }
292
        });
293
      });
294
    } else {
295
      return this;
296
    }
297
  };
298
299
  // Do something with inputs
300
  function operate(input, direct, method) {
301
    var node = input[0],
302
      state = /er/.test(method) ? _indeterminate : /bl/.test(method) ? _disabled : _checked,
303
      active = method == _update ? {
304
        checked: node[_checked],
305
        disabled: node[_disabled],
306
        indeterminate: input.attr(_indeterminate) == 'true' || input.attr(_determinate) == 'false'
307
      } : node[state];
308
309
    // Check, disable or indeterminate
310
    if (/^(ch|di|in)/.test(method) && !active) {
311
      on(input, state);
312
313
    // Uncheck, enable or determinate
314
    } else if (/^(un|en|de)/.test(method) && active) {
315
      off(input, state);
316
317
    // Update
318
    } else if (method == _update) {
319
320
      // Handle states
321
      for (var state in active) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
Comprehensibility Naming Best Practice introduced by
The variable state already seems to be declared on line 302. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
322
        if (active[state]) {
323
          on(input, state, true);
324
        } else {
325
          off(input, state, true);
326
        }
327
      }
328
    } else if (!direct || method == 'toggle') {
329
330
      // Helper or label was clicked
331
      if (!direct) {
332
        input[_callback]('ifClicked');
333
      }
334
        // Toggle checked state
335
      if (active) {
336
        if (node[_type] !== _radio) {
337
          off(input, state);
338
        }
339
      } else {
340
        on(input, state);
341
      }
342
    }
343
  }
344
    // Add checked, disabled or indeterminate state
345
  function on(input, state, keep) {
346
    var node = input[0],
347
      parent = input.parent(),
348
      checked = state == _checked,
349
      indeterminate = state == _indeterminate,
350
      disabled = state == _disabled,
351
      callback = indeterminate ? _determinate : checked ? _unchecked : 'enabled',
352
      regular = option(input, callback + capitalize(node[_type])),
353
      specific = option(input, state + capitalize(node[_type]));
354
355
    // Prevent unnecessary actions
356
    if (node[state] !== true) {
357
358
      // Toggle assigned radio buttons
359
      if (!keep && state == _checked && node[_type] == _radio && node.name) {
360
        var form = input.closest('form'),
361
          inputs = 'input[name="' + node.name + '"]';
362
363
        inputs = form.length ? form.find(inputs) : $(inputs);
364
365
        inputs.each(function() {
366
          if (this !== node && $(this).data(_iCheck)) {
367
            off($(this), state);
368
          }
369
        });
370
      }
371
        // Indeterminate state
372
      if (indeterminate) {
373
374
        // Add indeterminate state
375
        node[state] = true;
376
377
        // Remove checked state
378
        if (node[_checked]) {
379
          off(input, _checked, 'force');
380
        }
381
          // Checked or disabled state
382
      } else {
383
384
        // Add checked or disabled state
385
        if (!keep) {
386
          node[state] = true;
387
        }
388
          // Remove indeterminate state
389
        if (checked && node[_indeterminate]) {
390
          off(input, _indeterminate, false);
391
        }
392
      }
393
        // Trigger callbacks
394
      callbacks(input, checked, state, keep);
395
    }
396
      // Add proper cursor
397
    if (node[_disabled] && !!option(input, _cursor, true)) {
398
      parent.find('.' + _iCheckHelper).css(_cursor, 'default');
399
    }
400
      // Add state class
401
    parent[_add](specific || option(input, state) || '');
402
403
    // Set ARIA attribute
404
    disabled ? parent.attr('aria-disabled', 'true') : parent.attr('aria-checked', indeterminate ? 'mixed' : 'true');
405
406
    // Remove regular state class
407
    parent[_remove](regular || option(input, callback) || '');
408
  }
409
    // Remove checked, disabled or indeterminate state
410
  function off(input, state, keep) {
411
    var node = input[0],
412
      parent = input.parent(),
413
      checked = state == _checked,
414
      indeterminate = state == _indeterminate,
415
      disabled = state == _disabled,
416
      callback = indeterminate ? _determinate : checked ? _unchecked : 'enabled',
417
      regular = option(input, callback + capitalize(node[_type])),
418
      specific = option(input, state + capitalize(node[_type]));
419
420
    // Prevent unnecessary actions
421
    if (node[state] !== false) {
422
423
      // Toggle state
424
      if (indeterminate || !keep || keep == 'force') {
425
        node[state] = false;
426
      }
427
        // Trigger callbacks
428
      callbacks(input, checked, callback, keep);
429
    }
430
      // Add proper cursor
431
    if (!node[_disabled] && !!option(input, _cursor, true)) {
432
      parent.find('.' + _iCheckHelper).css(_cursor, 'pointer');
433
    }
434
      // Remove state class
435
    parent[_remove](specific || option(input, state) || '');
436
437
    // Set ARIA attribute
438
    disabled ? parent.attr('aria-disabled', 'false') : parent.attr('aria-checked', 'false');
439
440
    // Add regular state class
441
    parent[_add](regular || option(input, callback) || '');
442
  }
443
    // Remove all traces
444
  function tidy(input, callback) {
445
    if (input.data(_iCheck)) {
446
447
      // Remove everything except input
448
      input.parent().html(input.attr('style', input.data(_iCheck).s || ''));
449
450
      // Callback
451
      if (callback) {
452
        input[_callback](callback);
453
      }
454
        // Unbind events
455
      input.off('.i').unwrap();
456
      $(_label + '[for="' + input[0].id + '"]').add(input.closest(_label)).off('.i');
457
    }
458
  }
459
    // Get some option
460
  function option(input, state, regular) {
461
    if (input.data(_iCheck)) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if input.data(_iCheck) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
462
      return input.data(_iCheck).o[state + (regular ? '' : 'Class')];
463
    }
464
  }
465
    // Capitalize some string
466
  function capitalize(string) {
467
    return string.charAt(0).toUpperCase() + string.slice(1);
468
  }
469
    // Executable handlers
470
  function callbacks(input, checked, callback, keep) {
471
    if (!keep) {
472
      if (checked) {
473
        input[_callback]('ifToggled');
474
      }
475
        input[_callback]('ifChanged')[_callback]('if' + capitalize(callback));
476
    }
477
  }
478
})(window.jQuery || window.Zepto);
479